package es.ucm.fdi.xtremeaudio.modelo.dao;

import java.awt.Image;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileNameExtensionFilter;

import javazoom.jlgui.basicplayer.BasicPlayer;

import org.jaudiotagger.audio.AudioFile;
import org.jaudiotagger.audio.AudioFileIO;
import org.jaudiotagger.audio.exceptions.CannotReadException;
import org.jaudiotagger.audio.exceptions.CannotWriteException;
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
import org.jaudiotagger.audio.flac.metadatablock.MetadataBlockDataPicture;
import org.jaudiotagger.audio.mp3.MP3File;
import org.jaudiotagger.tag.FieldDataInvalidException;
import org.jaudiotagger.tag.FieldKey;
import org.jaudiotagger.tag.KeyNotFoundException;
import org.jaudiotagger.tag.Tag;
import org.jaudiotagger.tag.TagException;
import org.jaudiotagger.tag.flac.FlacTag;
import org.jaudiotagger.tag.vorbiscomment.VorbisCommentFieldKey;
import org.jaudiotagger.tag.vorbiscomment.VorbisCommentTag;
import org.jaudiotagger.tag.vorbiscomment.util.Base64Coder;

import es.ucm.fdi.xtremeaudio.controlador.Controlador;
import es.ucm.fdi.xtremeaudio.modelo.beans.Bean;
import es.ucm.fdi.xtremeaudio.modelo.beans.Biblioteca;
import es.ucm.fdi.xtremeaudio.modelo.beans.Cancion;
import es.ucm.fdi.xtremeaudio.modelo.beans.Playlist;
import es.ucm.fdi.xtremeaudio.modelo.excepciones.ExcepcionDao;
import es.ucm.fdi.xtremeaudio.modelo.excepciones.ReaderException;
import es.ucm.fdi.xtremeaudio.modelo.fachada.Fachada;
import es.ucm.fdi.xtremeaudio.modelo.parser.ParserSAXBiblio;
import es.ucm.fdi.xtremeaudio.modelo.parser.ParserSAXLista;
import es.ucm.fdi.xtremeaudio.modelo.utils.AbrirDirectorio;
import es.ucm.fdi.xtremeaudio.vista.AccionMenuContextual;
import es.ucm.fdi.xtremeaudio.vista.FormPrincipal;
import es.ucm.fdi.xtremeaudio.vista.JMenuItemContextual;

/**
 * <ul>
 * <li><b>Proyecto:</b> XtremeAudio</li>
 * <li><b>Grupo:</b> 7 <i>(4&deg;B)</i></li>
 * <li><b>Asignatura:</b> Ingenier&iacute;a del Software</li>
 * <li><b>Curso:</b> 2010/2011</li>
 * <li><b>Centro:</b> Universidad Complutense de Madrid</li>
 * </ul>
 * 
 * El <b>Dao</b> es la clase b&aacute;sica para representar el modelo de datos,
 * dentro del uso del patr&oacute;n MVC con Fachada. Se encarga del procesamiento de
 * todos los datos del modelo de la aplicaci&oacute;n de forma independiente de
 * la vista, desde la importaci&oacute;n de archivos hasta toda la funcionalidad para
 * reproducci&oacute;n, pasando por la organizaci&oacute;n de la biblioteca (carga,
 * actualizaci&oacute;n y guardado). Todas las &oacute;rdenes le llegan desde el
 * controlador y son enviadas desde el Dao hacia &eacute;ste a trav&eacute;s de
 * la fachada, que tambi&eacute;n se encarga de capturar y tratar las
 * excepciones que le lanza el propio Dao.<br/>
 * El tratamiento de tags est&aacute; basado en el uso de la librer&iacute;a <a
 * href="http://www.jthink.net/jaudiotagger">jaudiotagger</a>.
 * 
 * @author &Aacute;lvaro G&oacute;mez Porris
 * @author Elena Hern&aacute;ndez Delgado
 * @author Mar&iacute;a Trinidad Mart&iacute;n Campos
 * @author Mois&eacute;s Mart&iacute;nez Montes
 * @author Mat&iacute;as Salinero Delgado
 * 
 * @version %I%, %G%
 * 
 * @since 1.0
 * 
 * @see Bean
 * @see Biblioteca
 * @see Controlador
 * @see Fachada
 */
public class Dao {

	private static int OFFSET = 50000; // En bytes

	// replaceAll por si hay espacios en blanco en la ruta
	private static String RUTA_BIBLIOTECA = System.getProperty("user.dir")+
		System.getProperty("file.separator")+"biblioteca.xml";  /*= Dao.class.getClassLoader()
			.getResource("biblioteca.xml").toString()
			.substring(6).replaceAll("%20", " ");*/

	private Bean bean = new Bean();

	/**
	 * Constructora por defecto de la clase. Se encarga de cargar la biblioteca
	 * a partir de un fichero XML cuya ruta es conocida internamente.
	 */
	public Dao() {
		/*try {
			cargarBiblioteca();
		} catch (ReaderException e) {
			e.printStackTrace();
		}*/
	}

	/**
	 * Ordena al reproductor que conmute su modo aleatorio.
	 * 
	 * @return El bean que representa el estado actual de la aplicaci&oacute;n.
	 * 
	 * @see Bean
	 */
	public Bean aleatorio() {
		Player player = bean.getPlayer();
		if (player != null) {
			player.setAleatorio(!player.isAleatorio());
			return bean;
		}
		return null;
	}

	/**
	 * Ordena al reproductor que establezca como la nueva canci&oacute;n en
	 * reproducci&oacute;n la anterior a la actual de la lista que est&eacute;
	 * sonando o una aleatoria si este modo est&aacute; activo.
	 * 
	 * @return El bean que representa el estado actual de la aplicaci&oacute;n.
	 * 
	 * @see Bean
	 */
	public Bean anterior() {
		boolean reproduciendo = false;
		if (bean.getPlayer() != null) {
			if ((bean.getEstado().equals("Reproduciendo"))
					|| ((bean.getEstado().equals("Pausado")))) {
				reproduciendo = true;
				Controlador.stop(); // Paramos, por si estaba reproduciendo
			}
			Biblioteca b = bean.getBiblioteca();
			EstadoRepetir repetir = bean.getPlayer().getRepetir();
			if (repetir != EstadoRepetir.R_UNO) {
				int indice = b.getIndiceListaSonando();
				boolean aleatorio = bean.getPlayer().isAleatorio();
				int tamanyo = 0;
				Random rand = new Random();
				int r = rand
						.nextInt((int) Calendar.getInstance().getTimeInMillis());
				if (indice == Biblioteca.BIBLIOTECA) {
					indice = b.getIndiceCancionActual();
					tamanyo = b.getCanciones().size();
					if (aleatorio) {
						r = r % tamanyo;
						b.setIndiceCancionActual(r);
					} else if (indice > 0) {
						b.setIndiceCancionActual(indice - 1);
					} else if ((repetir == EstadoRepetir.R_TODO) && (indice == 0)) {
						b.setIndiceCancionActual(tamanyo - 1);
					}
				} else if (indice == Biblioteca.BUSQUEDA) {
					indice = b.getIndiceBusqueda();
					tamanyo = b.getBusqueda().size();
					if (aleatorio) {
						r = r % tamanyo;
						b.setIndiceBusqueda(r);
					} else if (indice > 0) {
						b.setIndiceBusqueda(indice - 1);
					} else if ((repetir == EstadoRepetir.R_TODO) && (indice == 0)) {
						b.setIndiceCancionActual(tamanyo - 1);
					}
				} else {
					Playlist p = b.getPlaylistSonando();
					indice = p.getIndiceActual();
					tamanyo = p.getCanciones().size();
					if (aleatorio) {
						r = r % tamanyo;
						p.setIndiceActual(r);
					} else if (indice > 0) {
						p.setIndiceActual(indice - 1);
					} else if ((repetir == EstadoRepetir.R_TODO) && (indice == 0)) {
						p.setIndiceActual(tamanyo - 1);
					}
				}
			}
			bean.setArchivo(new File(b.getCancionSonando().getPath()));
			cargaArchivo();
			if (reproduciendo) {
				Controlador.playPausa();
			}
			return bean;
		}
		return null;
	}

	/**
	 * Ordena al reproductor que avance la reproducci&oacute;n actual una serie
	 * de bytes conocidos de forma privada.
	 * 
	 * @return El bean que representa el estado actual de la aplicaci&oacute;n.
	 * 
	 * @see Bean
	 */
	public Bean avanzar() {
		Player player = bean.getPlayer();
		if (player != null) {
			bean.getTimer().stop();
			int avance = player.getBytesRead() + OFFSET;
			player.seek(avance);
			bean.setTiempoActual(bytesASegs(avance));
			if (bean.getEstado().equals("Reproduciendo")) {
				bean.getTimer().start();
			}
			return bean;
		}
		return null;
	}

	/**
	 * Busca la cadena de texto indicada entre los campos de t&iacute;tulo,
	 * &aacute;lbum y artista de las canciones de la lista general de la
	 * biblioteca.
	 * 
	 * @param texto
	 *            El texto a buscar.
	 * 
	 * @return El bean que representa el estado actual de la aplicaci&oacute;n.
	 * 
	 * @see Bean
	 * @see Biblioteca
	 */
	public Bean buscar(String texto) {
		Biblioteca b = bean.getBiblioteca();
		if (b != null) {
			ArrayList<Cancion> cancionesBiblio = b.getCanciones();
			b.setBusqueda(new ArrayList<Integer>());
			Iterator<Cancion> it = cancionesBiblio.iterator();
			while (it.hasNext()) {
				Cancion c = it.next();
				if (c.tieneCampo(texto)) {
					b.getBusqueda().add(c.getId());
				}
			}
			if (!b.getBusqueda().isEmpty()) {
				b.setIndiceListaActual(Biblioteca.BUSQUEDA);
				b.setIndiceBusqueda(0);
			} else {
				b.setIndiceBusqueda(-1);
			}
			return bean;
		}
		return null;
	}

	/**
	 * Cambia la lista de reproducci&oacute;n (biblioteca incluida;
	 * b&uacute;squeda excluida) que se est&eacute; mostrando en la vista
	 * actualmente, en funci&oacute;n de un evento en el &aacute;rbol de la
	 * misma.
	 * 
	 * @param idLista
	 *            El identificador de la nueva lista.
	 * 
	 * @return El bean que representa el estado actual de la aplicaci&oacute;n.
	 * 
	 * @see Bean
	 * @see Biblioteca
	 * @see FormPrincipal
	 */
	public Bean cambiaLista(int idLista) {
		Biblioteca b = bean.getBiblioteca();
		if ((idLista >= -1) && (idLista < b.getPlaylists().size())) {
			b.setIndiceListaActual(idLista);
			return bean;
		}
		return null;
	}

	/**
	 * Cambia el tiempo actual de reproducci&oacute;n.
	 * 
	 * @param segundos
	 *            Nuevo n&uacute;mero de segundos de la canci&oacute;n actual.
	 * 
	 * @return El bean que representa el estado actual de la aplicaci&oacute;n.
	 * 
	 * @see Bean
	 */
	public Bean cambiaTiempo(int segundos) {
		Player player = bean.getPlayer();
		if (player != null) {
			bean.getTimer().stop();
			int bytes = segsABytes(segundos);
			player.seek(bytes);
			bean.setTiempoActual(bytesASegs(bytes));			
			if (player.getStatus() == BasicPlayer.PLAYING) {
				bean.getTimer().start();
			}
			return bean;
		}
		return null;
	}

	/**
	 * M&eacute;todo que se ejecuta al arrancar la aplicaci&oacute;n y que se
	 * encarga de obtener el estado de la &uacute;ltima biblioteca guardada (en
	 * formato <i>XML</i> e &quot;inaccesible&quot; para el usuario) y crear el
	 * objeto {@link Biblioteca} asociado. Si el archivo de la biblioteca no
	 * existe, lo crea con una biblioteca vac&iacute;a.
	 * 
	 * @return El bean que representa el estado actual de la aplicaci&oacute;n.
	 * 
	 * @throws ReaderException
	 * 
	 * @see Bean
	 */
	public Bean cargarBiblioteca() throws ReaderException {
		File f = new File(RUTA_BIBLIOTECA);
		if (f.exists()) {
			if (f.length() > 0) {
				ParserSAXBiblio oParserSaxB = new ParserSAXBiblio();
				Biblioteca b = oParserSaxB.vParseFile(RUTA_BIBLIOTECA);
				if (!b.esVacia()) {
					b.setIndiceCancionActual(0);
					b.setIndiceListaActual(Biblioteca.BIBLIOTECA);
					b.setIndiceListaSonando(Biblioteca.BIBLIOTECA);
					for (int i = 0; i < b.getPlaylists().size(); i++) {
						ParserSAXLista oParserSaxL = new ParserSAXLista();
						Playlist p = b.getPlaylist(i);
						String path = p.getRuta();
						p.setCanciones(oParserSaxL.vParseFile(path));
						p.setIndiceActual(0);
					}
					bean.setBiblioteca(b);
					bean.setArchivo(new File(b.getCancionSonando().getPath()));
					cargaCaratulas();
					cargaArchivo();
				}
			} else {
				guardaBiblioteca();
			}
		} else { // Si no existe, lo crea
			guardaBiblioteca();
		}
		return bean;
	}

	/**
	 * Crea una nueva lista de reproducci&oacute;n (vac&iacute;a) en la
	 * biblioteca.
	 * 
	 * @param padre
	 *            El {@link JFrame} padre del JFileChooser a mostrar.
	 * 
	 * @return El bean que representa el estado actual de la aplicaci&oacute;n.
	 * 
	 * @see Bean
	 */
	public Bean crearLista(JFrame padre) {
		JFileChooser chooser;
		String dirUsuario = System.getenv("USERPROFILE");
		if (dirUsuario != null) {
			chooser = new JFileChooser(dirUsuario);
		} else {
			chooser = new JFileChooser();
		}
		final FileNameExtensionFilter filtro = new FileNameExtensionFilter(
				"Archivo XML", "xml");
		chooser.setFileFilter(filtro);
		chooser.setDialogTitle("Crear lista");
		int returnVal = chooser.showSaveDialog(padre);
		if (returnVal == JFileChooser.APPROVE_OPTION) {
			String path = chooser.getSelectedFile().getAbsolutePath();
			if (!(path.endsWith(".xml"))) {
				path += ".xml";
			}
			String nombre = path.substring(path.lastIndexOf('\\') + 1,
					path.lastIndexOf('.'));
			int id = bean.getBiblioteca().getPlaylists().size();
			ArrayList<Integer> canciones = new ArrayList<Integer>();
			Playlist listaNueva = new Playlist(id, canciones, nombre, path, 0);
			bean.getBiblioteca().getPlaylists().add(listaNueva);
			int indiceNueva = bean.getBiblioteca().getPlaylists().size() - 1;
			bean.getBiblioteca().setIndiceListaActual(indiceNueva);
			guardaLista();
			guardaBiblioteca();
		}
		return bean;
	}

	/**
	 * Ordena al reproductor que reproduzca la canci&oacute;n relativa a la
	 * lista actual mostrada en la vista, en funci&oacute;n de la fila donde se
	 * produjo el evento.
	 * 
	 * @param fila
	 *            Fila donde se hizo doble click o puls&oacute; la
	 *            tecla<i>Enter</i>.
	 * 
	 * @return El bean que representa el estado actual de la aplicaci&oacute;n.
	 * 
	 * @see Bean
	 */
	public Bean dobleClickIntro(int fila) {
		String path = null;
		Biblioteca b = bean.getBiblioteca();
		int indice = b.getIndiceListaActual();
		b.setIndiceListaSonando(indice);
		if (indice == Biblioteca.BIBLIOTECA) {
			if (fila != -1) {
				b.setIndiceCancionActual(fila);
				path = b.getCancionSonando().getPath();
			}
		} else if (indice == Biblioteca.BUSQUEDA) {
			if (fila != -1) {
				b.setIndiceBusqueda(fila);
				path = b.getCanciones().get(b.getBusqueda().get(fila))
						.getPath();
			}
		} else {
			Playlist p = b.getPlaylistActual();
			if (fila != -1) {
				p.setIndiceActual(fila);
				path = b.getCanciones().get(p.getCanciones().get(fila))
						.getPath();
			}
		}
		if (path != null) {
			bean.setArchivo(new File(path));
			cargaArchivo();
			stop();
			Controlador.playPausa();
			return bean;
		}
		return bean;
	}

	/**
	 * Elimina de la lista las canciones seleccionadas (excepto en la lista
	 * general de canciones de la biblioteca y la lista de b&uacute;squeda).
	 * 
	 * @return El bean actualizado.
	 */
	public Bean eliminar() {
		if (bean.getBiblioteca().getIndiceListaActual() == Biblioteca.PLAYLIST) {
			int[] f = Controlador.vistaPrincipal.getTablaLista()
					.getSelectedRows();
			int borrados = 0;
			for (int i = 0; i < f.length; i++) {
				ArrayList<Integer> canciones = bean.getBiblioteca()
						.getPlaylistActual().getCanciones();
				if ((f[i] - borrados) < canciones.size()) {
					canciones.remove(f[i] - borrados);
					borrados++;
				}
			}
		}
		return bean;
	}

	/**
	 * Genera tantos {@link JMenuItemContextual} como listas de
	 * reproducci&oacute;n haya en la biblioteca, asociando un identificador a
	 * cada uno para relacionarlo con la lista adecuada. Cada uno de
	 * &eacute;stos lleva incluido un {@link AccionMenuContextual} para poder
	 * a&ntilde;adir archivos a la lista correspondiente.<br/>
	 * Para ello se vale de una o varias filas seleccionadas en la lista
	 * mostrada en {@link Controlador#vistaPrincipal} (que representa a la lista
	 * general de la biblioteca), para ser a&ntilde;adidas donde corresponda.
	 * 
	 * @param menu
	 *            Men&uacute; base al que agregar &iacute;tems.
	 * @param filas
	 *            Filas seleccionadas.
	 * 
	 * @return El bean que representa el estado actual de la aplicaci&oacute;n.
	 * 
	 * @see AccionMenuContextual
	 * @see Bean
	 * @see Biblioteca
	 * @see JMenuItemContextual
	 */
	public Bean generaMenuContextual(JMenu menu, int[] filas) {
		Biblioteca b = bean.getBiblioteca();
		if (b != null) {
			menu.removeAll();
			ArrayList<Playlist> listas = b.getPlaylists();
			Iterator<Playlist> itP = listas.iterator();
			while (itP.hasNext()) {
				Playlist p = itP.next();
				JMenuItemContextual menuItem = new JMenuItemContextual(
						p.getNombre(), p.getId());
				if (b.getIndiceListaActual() == Biblioteca.BIBLIOTECA) {
					menu.setText("A\u00f1adir a la lista...");
					menuItem.addActionListener(new AccionMenuContextual(p
							.getId(), listas, filas));
					menu.add(menuItem);
				} else if (b.getIndiceListaActual() == Biblioteca.BUSQUEDA) {
					Iterator<Integer> itB = b.getBusqueda().iterator();
					int[] filasAux = new int[b.getBusqueda().size()];
					int pos = 0;
					while (itB.hasNext()) {
						int idCancion = itB.next();
						filasAux[pos] = idCancion;
						pos++;
					}
					menu.setText("A\u00f1adir a la lista...");
					menuItem.addActionListener(new AccionMenuContextual(p
							.getId(), listas, filasAux));
					menu.add(menuItem);
				} else {
					menu.setText("Eliminar");
					menu.setVisible(true);
					menu.addMouseListener(new MouseAdapter() {

						@Override
						public void mousePressed(MouseEvent e) {
							eliminar();
						}
					});
					break;
				}
			}
			return bean;
		}
		return null;
	}

	/**
	 * Accesora del bean que representa el estado actual de la
	 * aplicaci&oacute;n.
	 * 
	 * @return bean
	 * 
	 * @see Bean
	 */
	public Bean getBean() {
		return bean;
	}

	/**
	 * Guarda una copia de la lista que se est&eacute; visualizando, concediendo
	 * la posibilidad de elegir un nombre distinto al original (salvo si es la
	 * lista general de canciones de la biblioteca o lal ista de
	 * b&uacute;squedas).
	 * 
	 * @param padre
	 *            El {@link JFrame} padre del JFileChooser a mostrar.
	 * 
	 * @return El bean que representa el estado actual de la aplicaci&oacute;n.
	 * 
	 * @see Bean
	 */
	public Bean guardaListaComo(JFrame padre) {
		if (bean.getBiblioteca().getIndiceListaActual() != -1) {
			JFileChooser chooser;
			String dirUsuario = System.getenv("USERPROFILE");
			if (dirUsuario != null) {
				chooser = new JFileChooser(dirUsuario);
			} else {
				chooser = new JFileChooser();
			}
			final FileNameExtensionFilter filtro = new FileNameExtensionFilter(
					"Archivo XML", "xml");
			chooser.setFileFilter(filtro);
			chooser.setDialogTitle("Guardar lista como");
			int returnVal = chooser.showSaveDialog(padre);
			if (returnVal == JFileChooser.APPROVE_OPTION) {
				String path = chooser.getSelectedFile().getAbsolutePath();
				if (!(path.endsWith(".xml"))) {
					path += ".xml";
				}
				Playlist listaActual = bean.getBiblioteca().getPlaylistActual();
				String nombre = path.substring(path.lastIndexOf('\\') + 1,
						path.lastIndexOf('.'));
				int id = bean.getBiblioteca().getPlaylists().size();
				Playlist listaNueva = new Playlist(id,
						listaActual.getCanciones(), nombre, path, 0);
				bean.getBiblioteca().getPlaylists().add(listaNueva);
				int indiceNueva = bean.getBiblioteca().getPlaylists().size() - 1;
				bean.getBiblioteca().setIndiceListaActual(indiceNueva);
				guardaLista();
				guardaBiblioteca();
			}
			return bean;
		}
		return null;
	}

	/**
	 * Guarda la lista que se est&eacute; visualizando (biblioteca o playlist)
	 * en el archivo <i>XML</i> correspondiente, salvo si es una
	 * b&uacute;squeda.
	 */
	public void guardar() {
		int indice = bean.getBiblioteca().getIndiceListaActual();
		if (indice != Biblioteca.BUSQUEDA) {
			if (indice == Biblioteca.BIBLIOTECA) {
				guardaBiblioteca();
			} else {
				guardaLista();
			}
		}
	}

	/**
	 * Almacena en el archivo correspondiente los nuevos valores para los tags
	 * editables del mismo obtenidos en el formulario mostrado al usuario para
	 * tal efecto ({@link Controlador#vistaTags}).
	 * 
	 * @param oCancion
	 *            Cancion creada s&oacute;lo con los valores de los tags
	 *            editables que se asignar&aacute;n a la canci&oacute;n
	 *            actualmente sonando en la biblioteca.
	 * 
	 * @return El bean que representa el estado actual de la aplicaci&oacute;n.
	 * 
	 * 
	 * @throws ExcepcionDao
	 *             Indica posibles excepciones en la obtenci&oacute;n de tags,
	 *             apertura o modificaci&oacute;n de archivo, etc., aglutinadas
	 *             en una sola excepci&oacute;n propia.
	 * 
	 * @see Bean
	 * @see Biblioteca
	 * @see MP3File
	 * @see Tag
	 */
	public Bean guardarTags(Cancion oCancion) throws ExcepcionDao {
		if (bean.getArchivo() != null) {
			if (oCancion.getFormato().equalsIgnoreCase("mp3")) {
				guardaMP3(oCancion);
			} else if (oCancion.getFormato().equalsIgnoreCase("ogg")) {
				guardaOGG(oCancion);
			} else if (oCancion.getFormato().equalsIgnoreCase("flac")) {
				guardaFlac(oCancion);
			}
			return bean;
		}
		return null;
	}

	/**
	 * Abre todos los archivos con extensi&oacute;n mp3 seleccionados por el
	 * usuario mediante un {@link JFileChooser} y, si todo es correcto, los
	 * importa a la lista general de canciones de la biblioteca.
	 * 
	 * @param padre
	 *            El {@link JFrame} padre del JFileChooser a mostrar.
	 * 
	 * @return El bean que representa el estado actual de la aplicaci&oacute;n.
	 * 
	 * @see Bean
	 * @see Biblioteca
	 */
	public Bean importarArchivos(JFrame padre) {
		JFileChooser chooser;
		String dirUsuario = System.getenv("USERPROFILE");
		if (dirUsuario != null) {
			chooser = new JFileChooser(dirUsuario);
		} else {
			chooser = new JFileChooser();
		}
		final FileNameExtensionFilter filtro = new FileNameExtensionFilter(
				"Audio mp3, ogg, wav y flac", "mp3", "ogg", "flac", "wav");
		chooser.setFileFilter(filtro);
		chooser.setDialogTitle("Importar archivos");
		chooser.setMultiSelectionEnabled(true);
		int returnVal = chooser.showOpenDialog(padre);
		if (returnVal == JFileChooser.APPROVE_OPTION) {
			if ((bean.getPlayer().getStatus() != BasicPlayer.UNKNOWN)
					|| (bean.getPlayer().getStatus() != BasicPlayer.STOPPED)) {
				stop(); // Paramos, por si estaba reproduciendo
			}
			File[] archivos = chooser.getSelectedFiles();
			for (int i = 0; i < archivos.length; i++) {
				Cancion cancion = new Cancion();
				cancion.setPath(archivos[i].getAbsolutePath());
				if (!bean.getBiblioteca().existeCancion(cancion.getPath())) {
					extraeTags(cancion, archivos[i]);
					bean.getBiblioteca().getCanciones().add(cancion);
				}
			}

			/*
			 * Si importamos por primera vez, nos situamos en la primera
			 * cancion. Si no (anyadimos), nos mantenemos en la que estabamos.
			 */
			int indice = bean.getBiblioteca().getIndiceCancionActual();
			if (indice == -1) {
				bean.getBiblioteca().setIndiceCancionActual(0);
			}
			bean.setArchivo(new File(bean.getBiblioteca().getCancionSonando()
					.getPath()));
			cargaArchivo();
			return bean;
		}
		return null;
	}

	/**
	 * Abre todos los archivos con extensi&oacute;n mp3 de la carpeta
	 * seleccionada por el usuario mediante un {@link JFileChooser} y, si todo
	 * es correcto, los importa a la lista general de canciones de la
	 * biblioteca.
	 * 
	 * @param padre
	 *            El {@link JFrame} padre del JFileChooser a mostrar.
	 * 
	 * @return El bean que representa el estado actual de la aplicaci&oacute;n.
	 * 
	 * @see Bean
	 * @see Biblioteca
	 */
	public Bean importarCarpeta(JFrame padre) {
		JFileChooser chooser;
		String dirUsuario = System.getenv("USERPROFILE");
		if (dirUsuario != null) {
			chooser = new JFileChooser(dirUsuario);
		} else {
			chooser = new JFileChooser();
		}
		chooser.setDialogTitle("Importar carpeta");
		chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
		int returnVal = chooser.showOpenDialog(padre);
		if (returnVal == JFileChooser.APPROVE_OPTION) {
			if ((bean.getPlayer().getStatus() != BasicPlayer.UNKNOWN)
					|| (bean.getPlayer().getStatus() != BasicPlayer.STOPPED)) {
				stop(); // Paramos, por si estaba reproduciendo
			}
			String pathDir = chooser.getSelectedFile().getAbsolutePath();
			File dir = new File(pathDir);
			String[] extensiones = new String[] { "mp3", "ogg", "flac", "wav" };
			recorreDirectorio(dir, extensiones);

			/*
			 * Si importamos por primera vez, nos situamos en la primera
			 * cancion. Si no (anyadimos), nos mantenemos en la que estabamos.
			 */
			int indice = bean.getBiblioteca().getIndiceCancionActual();
			if (indice == -1) {
				bean.getBiblioteca().setIndiceCancionActual(0);
			}
			bean.setArchivo(new File(bean.getBiblioteca().getCancionSonando()
					.getPath()));
			cargaArchivo();
			return bean;
		}
		return null;
	}

	/**
	 * Limpia (elimina) todos los elementos de la lista actual (excepto en la
	 * lista general de canciones de la biblioteca y la lista de
	 * b&uacute;squeda).
	 * 
	 * @return El bean actualizado.
	 */
	public Bean limpiar() {
		if (bean.getBiblioteca().getIndiceListaActual() == Biblioteca.PLAYLIST) {
			ArrayList<Integer> canciones = bean.getBiblioteca()
					.getPlaylistActual().getCanciones();
			int total = canciones.size();
			for (int i = 0; i < total; i++) {
				canciones.remove(0);
			}
		}
		return bean;
	}

	/**
	 * Conmuta el sonido de la reproducci&oacute;n entre el estado
	 * <i>ON/OFF</i>.
	 * 
	 * @return El bean que representa el estado actual de la aplicaci&oacute;n.
	 * 
	 * @see Bean
	 */
	public Bean mute() {
		Player player = bean.getPlayer();
		if (player != null) {
			player.mute();
			return bean;
		}
		return null;
	}

	/**
	 * Realiza la acci&oacute;n asociada a eventos que manejan el play.
	 * <p>
	 * Si el reproductor est&aacute; parado o pausado, lo inicia o
	 * contin&uacute;a, respectivamente.<br/>
	 * Si estaba reproduciendo, lo pausa.
	 * </p>
	 * 
	 * @return El bean que representa el estado actual de la aplicaci&oacute;n.
	 * 
	 * @see Bean
	 */
	public Bean playPausa() {
		Player player = bean.getPlayer();
		System.out.println(Integer.toString(bean.getBiblioteca()
				.getIndiceListaSonando()));
		if (player != null) {
			String estado = bean.getEstado();
			if (estado.equals(Bean.REPRODUCIENDO)) {
				bean.getTimer().stop();
				player.pause();
				bean.setEstado(Bean.PAUSADO);
			} else if (estado.equals(Bean.PAUSADO)) {
				bean.getTimer().start();
				player.resume();
				bean.setEstado(Bean.REPRODUCIENDO);
			} else if (estado.equals(Bean.PARADO)) {
				bean.getTimer().start();
				player.play();
				bean.setEstado(Bean.REPRODUCIENDO);
			}
		}
		return bean;
	}

	/**
	 * Alterna el modo de repetici&oacute;n de reproducci&oacute;n siguiendo el
	 * ciclo <i>OFF-Uno-Todo</i>.
	 * 
	 * @return El bean que representa el estado actual de la aplicaci&oacute;n.
	 * 
	 * @see Bean
	 */
	public Bean repetir() {
		Player player = bean.getPlayer();
		if (player != null) {
			switch (player.getRepetir()) {
			case R_OFF:
				player.setRepetir(EstadoRepetir.R_UNO);
				break;
			case R_UNO:
				player.setRepetir(EstadoRepetir.R_TODO);
				break;
			case R_TODO:
				player.setRepetir(EstadoRepetir.R_OFF);
			}
		}
		return bean;
	}

	/**
	 * Ordena al reproductor que retroceda la reproducci&oacute;n actual una
	 * serie de bytes conocidos de forma privada.
	 * 
	 * @return El bean que representa el estado actual de la aplicaci&oacute;n.
	 * 
	 * @see Bean
	 */
	public Bean retroceder() {
		Player player = bean.getPlayer();
		if (player != null) {
			bean.getTimer().stop();
			int avance = player.getBytesRead() - OFFSET;
			player.seek(avance);
			bean.setTiempoActual(bytesASegs(avance));
			if (bean.getEstado().equals(Bean.REPRODUCIENDO)) {
				bean.getTimer().start();
			}
			return bean;
		}
		return null;
	}

	/**
	 * Modificadora de bean.
	 * 
	 * @param bean
	 *            El nuevo bean.
	 */
	public void setBean(Bean bean) {
		this.bean = bean;
	}

	/**
	 * Establece el volumen de la reproducci&oacute;n.
	 * 
	 * @param vol
	 *            El nuevo volumen.
	 * 
	 * @return El bean que representa el estado actual de la aplicaci&oacute;n.
	 * 
	 * @see Bean
	 */
	public Bean setGanancia(int vol) {
		Player player = bean.getPlayer();
		if (player != null) {
			player.setGanancia(Double.valueOf(vol) / Double.valueOf(100));
		}
		return bean;
	}

	/**
	 * Ordena al reproductor que establezca como la nueva canci&oacute;n en
	 * reproducci&oacute;n la siguiente a la actual de la lista que est&eacute;
	 * sonando o una aleatoria si este modo est&aacute; activo.
	 * 
	 * @return El bean que representa el estado actual de la aplicaci&oacute;n.
	 * 
	 * @see Bean
	 */
	public Bean siguiente() {
		boolean reproduciendo = false;
		if (bean.getPlayer() != null) {
			if ((bean.getEstado().equals(Bean.REPRODUCIENDO))
					|| ((bean.getEstado().equals(Bean.PAUSADO)))) {
				reproduciendo = true;
				Controlador.stop(); // Paramos, por si estaba reproduciendo
			}
			Biblioteca b = bean.getBiblioteca();
			EstadoRepetir repetir = bean.getPlayer().getRepetir();
			if (repetir != EstadoRepetir.R_UNO){				
				int indice = b.getIndiceListaSonando();
				boolean aleatorio = bean.getPlayer().isAleatorio();
				int tamanyo = 0;
				Random rand = new Random();
				int r = rand
						.nextInt((int) Calendar.getInstance().getTimeInMillis());
				if (indice == Biblioteca.BIBLIOTECA) {
					indice = b.getIndiceCancionActual();
					tamanyo = b.getCanciones().size();
					if (aleatorio) {
						r = r % tamanyo;
						b.setIndiceCancionActual(r);
					} else if (indice < (tamanyo - 1)) {
						b.setIndiceCancionActual(indice + 1);
					} else if ((repetir == EstadoRepetir.R_TODO) && (indice == tamanyo - 1)) {
						b.setIndiceCancionActual(0);
					}
				} else if (indice == Biblioteca.BUSQUEDA) {
					indice = b.getIndiceBusqueda();
					tamanyo = b.getBusqueda().size();
					if (aleatorio) {
						r = r % tamanyo;
						b.setIndiceBusqueda(r);
					} else if (indice < (tamanyo - 1)) {
						b.setIndiceBusqueda(indice + 1);
					}  else if ((repetir == EstadoRepetir.R_TODO) && (indice == tamanyo - 1)) {
						b.setIndiceCancionActual(0);
					}
				} else {
					Playlist p = b.getPlaylistSonando();
					indice = p.getIndiceActual();
					tamanyo = p.getCanciones().size();
					if (aleatorio) {
						r = r % tamanyo;
						p.setIndiceActual(r);
					} else if (indice < (tamanyo - 1)) {
						p.setIndiceActual(indice + 1);
					}  else if ((repetir == EstadoRepetir.R_TODO) && (indice == tamanyo - 1)) {
						p.setIndiceActual(0);
					}
				}
			}
			bean.setArchivo(new File(b.getCancionSonando().getPath()));
			cargaArchivo();
			if (reproduciendo) {
				Controlador.playPausa();
			}
			return bean;
		}
		return null;
	}

	/**
	 * Ordena al reproductor que se detenga y establece el tiempo actual a 0 y
	 * el estado como &quot;Parado&quot;.
	 * 
	 * @return El bean que representa el estado actual de la aplicaci&oacute;n.
	 * 
	 * @see Bean
	 */
	public Bean stop() {
		Player player = bean.getPlayer();
		bean.setTiempoActual(0);
		bean.getTimer().stop();
		player.stop();
		bean.setEstado(Bean.PARADO);
		return bean;
	}

	/*--- METODOS PRIVADOS ---*/
	private int bytesASegs(int bytesActual) {
		return (int) (bean.getBiblioteca().getCancionSonando().getDuracion() * ((float) bytesActual / (float) bean
				.getArchivo().length()));
	}

	private void cargaArchivo() {
		bean.setTiempoActual(0);
		bean.getPlayer().open(bean.getArchivo().toString());
	}

	private void cargaCaratulas() {
		Iterator<Cancion> it = bean.getBiblioteca().getCanciones().iterator();
		while (it.hasNext()) {
			Cancion c = it.next();
			File file = new File(c.getPath());
			AudioFile audioFile = null;
			try {
				audioFile = AudioFileIO.read(file);
			} catch (CannotReadException e1) {
				e1.printStackTrace();
			} catch (IOException e1) {
				e1.printStackTrace();
			} catch (TagException e1) {
				e1.printStackTrace();
			} catch (ReadOnlyFileException e1) {
				e1.printStackTrace();
			} catch (InvalidAudioFrameException e1) {
				e1.printStackTrace();
			}
			VorbisCommentTag vTag = null;
			BufferedImage imagen = null;
			Image caratula = null;
			try {
				if (c.getFormato().equalsIgnoreCase("mp3")) {
					MP3File mp3f = (MP3File) audioFile;
					Tag tag = mp3f.getTag();
					if ((tag.getFirstArtwork() != null)
							&& (tag.getFirstArtwork().getImage() != null)) {
						imagen = tag.getFirstArtwork().getImage();
					}
				} else if (c.getFormato().equalsIgnoreCase("ogg")) {
					vTag = (VorbisCommentTag) audioFile.getTag();
					String imageData = vTag
							.getFirst(VorbisCommentFieldKey.COVERART);
					imagen = ImageIO
							.read(ImageIO
									.createImageInputStream(new ByteArrayInputStream(
											Base64Coder.decode(imageData
													.toCharArray()))));
				} else if (c.getFormato().equalsIgnoreCase("flac")) {
					FlacTag fTag = (FlacTag) audioFile.getTag();
					MetadataBlockDataPicture img = fTag.getImages().get(0);
					if (img != null) {
						imagen = ImageIO
								.read(ImageIO
										.createImageInputStream(new ByteArrayInputStream(
												img.getImageData())));
					}
				}
				if (imagen != null) {
					caratula = imagen.getScaledInstance(123, 127,
							Image.SCALE_AREA_AVERAGING);
				}
				c.setCaratula(caratula);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	private String charAEntidad(String cadena) {
		String cadenaXML = new String(cadena);
		if ((cadena.trim().equals(""))
				|| (cadena.trim().compareToIgnoreCase("desconocido") == 0)) {
			cadenaXML = "&desc;";
		} else {
			cadenaXML.replaceAll("<", "&lt;");
			cadenaXML.replaceAll(">", "&gt;");
			cadenaXML.replaceAll("&", "&amp;");
			cadenaXML.replaceAll("\"", "&quot;");
			cadenaXML.replaceAll("'", "&apos;");
		}
		return cadenaXML;
	}

	private void extraeTags(Cancion cancion, File file) {
		String nombre = file.getName();
		String formato = nombre.substring(nombre.lastIndexOf('.') + 1);
		cancion.setFormato(formato);
		if (formato.equalsIgnoreCase("mp3")) {
			procesaMP3(file, cancion);
		} else if (formato.equalsIgnoreCase("ogg")) {
			procesaOGG(file, cancion);
		} else if (formato.equalsIgnoreCase("flac")) {
			procesaFlac(file, cancion);
		} else {
			int id = bean.getBiblioteca().getCanciones().size();
			cancion.setId(id);
			cancion.setTitulo("Desconocido");
			cancion.setAlbum("Desconocido");
			cancion.setAutor("Desconocido");
			cancion.setGenero("Desconocido");
			cancion.setAnyo("Desconocido");
			cancion.setPista("Desconocido");
			cancion.setCaratula(null);
			int segs = bytesASegs((int) file.length());
			cancion.setDuracion(segs);
			cancion.setStringDuracion(Controlador.tiempoToString(segs));
		}
	}

	private void getVorbisTags(Cancion cancion, AudioFile audioFile,
			VorbisCommentTag tag, BufferedImage imagen) {
		int id = bean.getBiblioteca().getCanciones().size();
		cancion.setId(id);

		Image caratula = null;
		if (imagen != null) {
			caratula = imagen.getScaledInstance(123, 127,
					Image.SCALE_AREA_AVERAGING);
		}
		cancion.setCaratula(caratula);

		String s = tag.getFirst(VorbisCommentFieldKey.ARTIST);
		if (s.equals("")) {
			s = "Desconocido";
		}
		cancion.setAutor(s);

		s = tag.getFirst(VorbisCommentFieldKey.ALBUM);
		if (s.equals("")) {
			s = "Desconocido";
		}
		cancion.setAlbum(s);

		s = tag.getFirst(VorbisCommentFieldKey.TITLE);
		if (s.equals("")) {
			s = "Desconocido";
		}
		cancion.setTitulo(s);

		s = tag.getFirst(VorbisCommentFieldKey.GENRE);
		if (s.equals("")) {
			s = "Desconocido";
		}
		cancion.setGenero(s);

		s = tag.getFirst(VorbisCommentFieldKey.DATE);
		if (s.equals("")) {
			s = "Desconocido";
		}
		cancion.setAnyo(s);

		s = tag.getFirst(VorbisCommentFieldKey.TRACKNUMBER);
		if (s.equals("")) {
			s = "Desconocido";
		}
		cancion.setPista(s);
		cancion.setDuracion(audioFile.getAudioHeader().getTrackLength());
		cancion.setStringDuracion(Controlador.tiempoToString(cancion
				.getDuracion()));
	}

	private void guardaBiblioteca() {
		try {
			FileWriter out = new FileWriter(new File(RUTA_BIBLIOTECA));
			/*out.write("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
			String dtd = Dao.class.getClassLoader()
					.getResource("biblioteca.dtd").toString().substring(6);
			out.write("<!DOCTYPE biblioteca SYSTEM \"" + dtd + "\">\n");*/
			out.write("<biblioteca>\n");

			ArrayList<Cancion> aCanciones = bean.getBiblioteca().getCanciones();
			Iterator<Cancion> itC = aCanciones.iterator();
			while (itC.hasNext()) {
				Cancion c = itC.next();
				out.write("\t<cancion id=\"" + c.getId() + "\">\n");
				out.write("\t\t<autor>" + charAEntidad(c.getAutor())
						+ "</autor>\n");
				out.write("\t\t<album>" + charAEntidad(c.getAlbum())
						+ "</album>\n");
				out.write("\t\t<titulo>" + charAEntidad(c.getTitulo())
						+ "</titulo>\n");
				out.write("\t\t<genero>" + charAEntidad(c.getGenero())
						+ "</genero>\n");
				out.write("\t\t<anyo>" + charAEntidad(c.getAnyo())
						+ "</anyo>\n");
				out.write("\t\t<pista>" + charAEntidad(c.getPista())
						+ "</pista>\n");
				out.write("\t\t<duracion>"
						+ charAEntidad(c.getStringDuracion()) + "</duracion>\n");
				out.write("\t\t<formato>" + charAEntidad(c.getFormato())
						+ "</formato>\n");
				out.write("\t\t<path>" + charAEntidad(c.getPath())
						+ "</path>\n");
				out.write("\t</cancion>\n");
			}

			ArrayList<Playlist> aPlaylists = bean.getBiblioteca()
					.getPlaylists();
			Iterator<Playlist> itP = aPlaylists.iterator();
			while (itP.hasNext()) {
				Playlist p = itP.next();
				out.write("\t<playlist id=\"" + p.getId() + "\">\n");
				out.write("\t\t<nombre>" + charAEntidad(p.getNombre())
						+ "</nombre>\n");
				out.write("\t\t<ruta>" + charAEntidad(p.getRuta())
						+ "</ruta>\n");
				out.write("\t</playlist>\n");
			}

			out.write("</biblioteca>\n");
			out.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	private void guardaFlac(Cancion oCancion) throws ExcepcionDao {
		AudioFile f = null;
		try {
			f = AudioFileIO.read(bean.getArchivo());
			FlacTag fTag = (FlacTag) f.getTag();
			VorbisCommentTag tag = (VorbisCommentTag) fTag
					.getVorbisCommentTag();
			setVorbisTags(oCancion, tag, f);
		} catch (CannotReadException e) {
			e.printStackTrace();
			throw new ExcepcionDao("CannotReadException");
		} catch (IOException e) {
			e.printStackTrace();
			throw new ExcepcionDao("IOException");
		} catch (FieldDataInvalidException e) {
			e.printStackTrace();
			throw new ExcepcionDao("FieldDataInvalidException");
		} catch (TagException e) {
			e.printStackTrace();
			throw new ExcepcionDao("TagException");
		} catch (ReadOnlyFileException e) {
			e.printStackTrace();
			throw new ExcepcionDao("ReadOnlyFileException");
		} catch (InvalidAudioFrameException e) {
			e.printStackTrace();
			throw new ExcepcionDao("InvalidAudioFrameException");
		} catch (KeyNotFoundException e) {
			e.printStackTrace();
		} catch (CannotWriteException e) {
			e.printStackTrace();
		}
	}

	private void guardaLista() {
		try {
			Playlist listaActual = bean.getBiblioteca().getPlaylistActual();
			FileWriter out = new FileWriter(new File(listaActual.getRuta()));
			/*out.write("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
			String dtd = Dao.class.getClassLoader().getResource("lista.dtd")
					.toString().substring(6);
			out.write("<!DOCTYPE lista SYSTEM \"" + dtd + "\">\n");*/
			out.write("<lista>\n");

			ArrayList<Integer> aIDs = listaActual.getCanciones();
			Iterator<Integer> itC = aIDs.iterator();
			while (itC.hasNext()) {
				Integer id = itC.next();
				out.write("\t<id>" + id.toString() + "</id>\n");
			}

			out.write("</lista>\n");
			out.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	private void guardaMP3(Cancion oCancion) throws ExcepcionDao {
		MP3File mp3f = null;
		try {
			mp3f = (MP3File) AudioFileIO.read(bean.getArchivo());
			Tag tag = mp3f.getID3v2TagAsv24();
			tag.setField(FieldKey.ARTIST, oCancion.getAutor());
			tag.setField(FieldKey.ALBUM, oCancion.getAlbum());
			tag.setField(FieldKey.TITLE, oCancion.getTitulo());
			tag.setField(FieldKey.GENRE, oCancion.getGenero());
			tag.setField(FieldKey.YEAR, oCancion.getAnyo());
			tag.setField(FieldKey.TRACK, oCancion.getPista());
			mp3f.setTag(tag);
			mp3f.save(bean.getArchivo());
			bean.getBiblioteca().getCancionSonando()
					.setAutor(tag.getFirst(FieldKey.ARTIST));
			bean.getBiblioteca().getCancionSonando()
					.setAlbum(tag.getFirst(FieldKey.ALBUM));
			bean.getBiblioteca().getCancionSonando()
					.setTitulo(tag.getFirst(FieldKey.TITLE));
			bean.getBiblioteca().getCancionSonando()
					.setGenero(tag.getFirst(FieldKey.GENRE));
			bean.getBiblioteca().getCancionSonando()
					.setAnyo(tag.getFirst(FieldKey.YEAR));
			bean.getBiblioteca().getCancionSonando()
					.setPista(tag.getFirst(FieldKey.TRACK));
			guardaBiblioteca();
		} catch (CannotReadException e) {
			e.printStackTrace();
			throw new ExcepcionDao("CannotReadException");
		} catch (IOException e) {
			e.printStackTrace();
			throw new ExcepcionDao("IOException");
		} catch (FieldDataInvalidException e) {
			e.printStackTrace();
			throw new ExcepcionDao("FieldDataInvalidException");
		} catch (TagException e) {
			e.printStackTrace();
			throw new ExcepcionDao("TagException");
		} catch (ReadOnlyFileException e) {
			e.printStackTrace();
			throw new ExcepcionDao("ReadOnlyFileException");
		} catch (InvalidAudioFrameException e) {
			e.printStackTrace();
			throw new ExcepcionDao("InvalidAudioFrameException");
		}
	}

	private void guardaOGG(Cancion oCancion) throws ExcepcionDao {
		AudioFile f = null;
		try {
			f = AudioFileIO.read(bean.getArchivo());
			VorbisCommentTag tag = (VorbisCommentTag) f.getTag();
			setVorbisTags(oCancion, tag, f);
		} catch (CannotReadException e) {
			e.printStackTrace();
			throw new ExcepcionDao("CannotReadException");
		} catch (IOException e) {
			e.printStackTrace();
			throw new ExcepcionDao("IOException");
		} catch (FieldDataInvalidException e) {
			e.printStackTrace();
			throw new ExcepcionDao("FieldDataInvalidException");
		} catch (TagException e) {
			e.printStackTrace();
			throw new ExcepcionDao("TagException");
		} catch (ReadOnlyFileException e) {
			e.printStackTrace();
			throw new ExcepcionDao("ReadOnlyFileException");
		} catch (InvalidAudioFrameException e) {
			e.printStackTrace();
			throw new ExcepcionDao("InvalidAudioFrameException");
		} catch (CannotWriteException e) {
			e.printStackTrace();
		}
	}

	private void procesaFlac(File file, Cancion cancion) {
		AudioFile audioFile = null;
		BufferedImage imagen = null;
		FlacTag fTag = null;
		VorbisCommentTag tag = null;
		try {
			audioFile = AudioFileIO.read(file);
			fTag = (FlacTag) audioFile.getTag();
			tag = fTag.getVorbisCommentTag();
			List<MetadataBlockDataPicture> imgs = fTag.getImages();
			if (imgs.size() > 0) {
				MetadataBlockDataPicture img = imgs.get(0);
				if (img != null) {
					imagen = ImageIO.read(ImageIO
							.createImageInputStream(new ByteArrayInputStream(
									img.getImageData())));
				}
			}
		} catch (CannotReadException e1) {
			e1.printStackTrace();
		} catch (IOException e1) {
			e1.printStackTrace();
		} catch (TagException e1) {
			e1.printStackTrace();
		} catch (ReadOnlyFileException e1) {
			e1.printStackTrace();
		} catch (InvalidAudioFrameException e1) {
			e1.printStackTrace();
		}
		getVorbisTags(cancion, audioFile, tag, imagen);
	}

	private void procesaMP3(File file, Cancion cancion) {
		MP3File mp3f = null;
		Tag tag = null;
		BufferedImage imagen = null;
		try {
			mp3f = (MP3File) AudioFileIO.read(file);
			tag = mp3f.getTag();
			if ((tag.getFirstArtwork() != null)
					&& (tag.getFirstArtwork().getImage() != null)) {
				imagen = tag.getFirstArtwork().getImage();
			}
		} catch (CannotReadException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (TagException e) {
			e.printStackTrace();
		} catch (ReadOnlyFileException e) {
			e.printStackTrace();
		} catch (InvalidAudioFrameException e) {
			e.printStackTrace();
		}
		int id = bean.getBiblioteca().getCanciones().size();
		cancion.setId(id);

		Image caratula = null;
		if (imagen != null) {
			caratula = imagen.getScaledInstance(123, 127,
					Image.SCALE_AREA_AVERAGING);
		}
		cancion.setCaratula(caratula);

		String s = tag.getFirst(FieldKey.ARTIST);
		if (s.equals("")) {
			s = "Desconocido";
		}
		cancion.setAutor(s);

		s = tag.getFirst(FieldKey.ALBUM);
		if (s.equals("")) {
			s = "Desconocido";
		}
		cancion.setAlbum(s);

		s = tag.getFirst(FieldKey.TITLE);
		if (s.equals("")) {
			s = "Desconocido";
		}
		cancion.setTitulo(s);

		s = tag.getFirst(FieldKey.GENRE);
		if (s.equals("")) {
			s = "Desconocido";
		}
		cancion.setGenero(s);

		s = tag.getFirst(FieldKey.YEAR);
		if (s.equals("")) {
			s = "Desconocido";
		}
		cancion.setAnyo(s);

		s = tag.getFirst(FieldKey.TRACK);
		if (s.equals("")) {
			s = "Desconocido";
		}
		cancion.setPista(s);

		cancion.setDuracion(mp3f.getMP3AudioHeader().getTrackLength());
		cancion.setStringDuracion(mp3f.getMP3AudioHeader()
				.getTrackLengthAsString());
	}

	private void procesaOGG(File file, Cancion cancion) {
		AudioFile audioFile = null;
		BufferedImage imagen = null;
		VorbisCommentTag tag = null;
		try {
			audioFile = AudioFileIO.read(file);
			tag = (VorbisCommentTag) audioFile.getTag();
			String imageData = tag.getFirst(VorbisCommentFieldKey.COVERART);
			imagen = ImageIO.read(ImageIO
					.createImageInputStream(new ByteArrayInputStream(
							Base64Coder.decode(imageData.toCharArray()))));
		} catch (CannotReadException e1) {
			e1.printStackTrace();
		} catch (IOException e1) {
			e1.printStackTrace();
		} catch (TagException e1) {
			e1.printStackTrace();
		} catch (ReadOnlyFileException e1) {
			e1.printStackTrace();
		} catch (InvalidAudioFrameException e1) {
			e1.printStackTrace();
		}
		getVorbisTags(cancion, audioFile, tag, imagen);
	}

	private void recorreDirectorio(File dir, String[] filtros) {
		AbrirDirectorio od = new AbrirDirectorio(dir, filtros);
		File f = null;
		int i;
		for (i = 0; i < od.getArchivosTotal().length; i++) {
			f = od.getArchivosTotal()[i];
			if (f.isDirectory()) {
				recorreDirectorio(f, filtros);
			}
		}
		for (i = 0; i < od.getArchivosFiltro().length; i++) {
			f = od.getArchivosFiltro()[i];
			Cancion cancion = new Cancion();
			cancion.setPath(f.getAbsolutePath());
			if (!bean.getBiblioteca().existeCancion(cancion.getPath())) {
				extraeTags(cancion, f);
				bean.getBiblioteca().getCanciones().add(cancion);
			}
		}
	}

	private int segsABytes(int segundosActual) {
		return (int) (bean.getArchivo().length() * ((float) segundosActual / (float) bean
				.getBiblioteca().getCancionSonando().getDuracion()));
	}

	private void setVorbisTags(Cancion oCancion, VorbisCommentTag tag,
			AudioFile f) throws KeyNotFoundException,
			FieldDataInvalidException, CannotWriteException {
		tag.setField(FieldKey.ARTIST, oCancion.getAutor());
		tag.setField(FieldKey.ALBUM, oCancion.getAlbum());
		tag.setField(FieldKey.TITLE, oCancion.getTitulo());
		tag.setField(FieldKey.GENRE, oCancion.getGenero());
		tag.setField(FieldKey.YEAR, oCancion.getAnyo());
		tag.setField(FieldKey.TRACK, oCancion.getPista());
		f.setTag(tag);
		f.commit();
		bean.getBiblioteca().getCancionSonando()
				.setAutor(tag.getFirst(FieldKey.ARTIST));
		bean.getBiblioteca().getCancionSonando()
				.setAlbum(tag.getFirst(FieldKey.ALBUM));
		bean.getBiblioteca().getCancionSonando()
				.setTitulo(tag.getFirst(FieldKey.TITLE));
		bean.getBiblioteca().getCancionSonando()
				.setGenero(tag.getFirst(FieldKey.GENRE));
		bean.getBiblioteca().getCancionSonando()
				.setAnyo(tag.getFirst(FieldKey.YEAR));
		bean.getBiblioteca().getCancionSonando()
				.setPista(tag.getFirst(FieldKey.TRACK));
		guardaBiblioteca();
	}

}